package com.hu.core.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.hu.core.context.AppContext;
import com.hu.core.util.DBUtil;
import com.hu.example.exception.DBException;
import com.hu.example.proxy.ConnectionHolder;
import com.mysql.jdbc.Statement;

public class JDBCTemplate<T> {
    public List<T> query(String sqlString, JDBCCallback<T> callback) {
        Connection conn = null;
        PreparedStatement pStatement = null;
        ResultSet rs = null;
        List<T> dataList = new ArrayList<>();
        boolean needMyClose = false;
        try {
            ConnectionHolder connectionHolder = (ConnectionHolder)AppContext.getContext().getObject("APP_REQUEST_THREAD_CONNECTION");
            if (null!=connectionHolder) {
                conn = connectionHolder.getConn();
            }

            if (null == conn) {
                conn = DBUtil.getConnection();
                needMyClose = true;
            }

            pStatement = conn.prepareStatement(sqlString);
            callback.setParams(pStatement);
            rs = pStatement.executeQuery();
            while (rs.next()){
                dataList.add(callback.toObject(rs));
            }

        } catch (SQLException e) {
            e.printStackTrace();
            throw new DBException();
        } finally {
            DBUtil.close(null, pStatement, rs);
            if (needMyClose) {
                DBUtil.close(conn, null, null);
            }
        }

        return dataList;
    }

    public T queryOne(String sqlString, JDBCCallback<T> callback) {
        List<T> data = query(sqlString, callback);
        if (null != data && !data.isEmpty()) {
            return data.get(0);
        }

        return null;
    }

    public int update(String sqlString, JDBCCallback<T> callback) {
        Connection conn = null;
        PreparedStatement pStatement = null;
        ResultSet rs = null;
        int count = 0;
        try {
            ConnectionHolder connectionHolder = (ConnectionHolder)AppContext.getContext().getObject("APP_REQUEST_THREAD_CONNECTION");
            conn = connectionHolder.getConn();
            pStatement = conn.prepareStatement(sqlString);
            callback.setParams(pStatement);
            count = pStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new DBException();
        } finally {
            DBUtil.close(conn, pStatement, rs);
        }

        return count;
    }

    public int insert(String sqlString, JDBCCallback<T> callback) {
        return insert(sqlString, callback, true);
    }

    public int insert(String sqlString, JDBCCallback<T> callback, boolean autoGenKey) {
        Connection conn = null;
        PreparedStatement pStatement = null;
        ResultSet rs = null;
        int id = 0;
        try {
            ConnectionHolder connectionHolder = (ConnectionHolder)AppContext.getContext().getObject("APP_REQUEST_THREAD_CONNECTION");
            conn = connectionHolder.getConn();
            if (autoGenKey) {
                pStatement = conn.prepareStatement(sqlString, Statement.RETURN_GENERATED_KEYS);
            } else {
                pStatement = conn.prepareStatement(sqlString);
            }

            callback.setParams(pStatement);
            pStatement.executeUpdate();
            if (autoGenKey) {
                rs = pStatement.getGeneratedKeys();
                if (rs.next()) {
                    id = rs.getInt(1);
                }
            }

            return id;
        } catch (SQLException e) {
            e.printStackTrace();
            throw new DBException();
        } finally {
            DBUtil.close(conn,pStatement,rs);
        }
    }

    /**
     * Get the column count
     * @param sqlString execute sql, the first column must be COUNT()
     * @param callback Use this to set parameters or get reslut set
     * @return Count
     */
    public int getCount(String sqlString, JDBCCallback<T> callback) {
        Connection conn = null;
        PreparedStatement pStatement = null;
        ResultSet rs = null;
        int count = 0;
        try {
            ConnectionHolder connectionHolder = (ConnectionHolder)AppContext.getContext().getObject("APP_REQUEST_THREAD_CONNECTION");
            conn = connectionHolder.getConn();
            pStatement = conn.prepareStatement(sqlString);
            callback.setParams(pStatement);
            rs = pStatement.executeQuery();
            if (rs.next()) {
                count = rs.getInt(1);
            }

        } catch (SQLException e) {
            e.printStackTrace();
            throw new DBException();
        } finally {
            DBUtil.close(conn, pStatement, rs);
        }

        return count;
    }

    /**
     * Get the column count
     * @param sqlString Execute sql, the first column must be COUNT
     * @return Count
     */
    public int getCount(String sqlString) {
        return getCount(sqlString, new JDBCAbstractCallback<T>() {});
    }

}
